package mt.dummy;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import mt.Order;
import mt.comm.ClientMessage;
import mt.comm.ClientSideMessage.Type;
import mt.comm.ServerComm;
import mt.comm.ServerMessage;
import mt.comm.ServerSideMessage;

/**
 * The Class ServerCommDummy.
 */
public class ServerCommDummy implements ServerComm {

        /** The Constant PORT. */
        private static final int PORT = 6000;

        /** The server socket. */
        private ServerSocket serverSocket;

        /** The socket. */
        private Socket socket;

        /** The client socket in. */
        private ObjectInputStream clientSocketIn;

        /** The client socket out. */
        private ObjectOutputStream clientSocketOut;
        // has messages type conected and newOrder
        /** The messages. */
        private BlockingQueue<ServerMessage> messages = new LinkedBlockingQueue<ServerMessage>();

        /** The clients. */
        public HashMap<String, ObjectOutputStream> clients = new HashMap<String, ObjectOutputStream>();

        private boolean userAlreadyExists = false;

        /*
         * (non-Javadoc)
         * 
         * @see mt.comm.ServerComm#start()
         */
        @Override
        public void start() {

                new Thread(new Runnable() {

                        @Override
                        public void run() {

                                try {
                                        // connect to port
                                        serverSocket = new ServerSocket(PORT);

                                        while (true) {
                                                // waiting for clients
                                                socket = serverSocket.accept();

                                                clientSocketOut = new ObjectOutputStream(
                                                                socket.getOutputStream());
                                                clientSocketIn = new ObjectInputStream(
                                                                socket.getInputStream());

                                                // deal with multiple clients

                                                new DealWithClientCommDummy(clientSocketOut,
                                                                clientSocketIn, ServerCommDummy.this).start();

                                        }

                                } catch (IOException e) {
                                        e.printStackTrace();
                                } finally {

                                        try {

                                                serverSocket.close();
                                                clientSocketIn.close();
                                                clientSocketOut.close();
                                        } catch (IOException e) {
                                                // Error - maybe not closed the server socket
                                        }
                                }

                        }
                }).start();

        }

        /*
         * (non-Javadoc)
         * 
         * @see mt.comm.ServerComm#hasNextMessage()
         */
        @Override
        public boolean hasNextMessage() {
                return !messages.isEmpty();
        }

        /*
         * (non-Javadoc)
         * 
         * @see mt.comm.ServerComm#getNextMessage()
         */
        @Override
        public ServerSideMessage getNextMessage() {

                return messages.poll();
        }

        /*
         * (non-Javadoc)
         * 
         * @see mt.comm.ServerComm#sendError(java.lang.String, java.lang.String)
         */
        @Override
        public void sendError(String toNickname, String error) {
                try {
                        ObjectOutputStream outC = clients.get(toNickname);
                        ClientMessage msg = new ClientMessage(Type.ERROR, "ERROR: " + error);
                        outC.writeObject(msg);

                } catch (IOException e) {
                        System.out.println("Error sending message to Client");
                }
        }

        /*
         * (non-Javadoc)
         * 
         * @see mt.comm.ServerComm#clientIsConnected(java.lang.String)
         */
        @Override
        public boolean clientIsConnected(String nickname) {
                return (clients.containsKey(nickname));
        }

        /*
         * (non-Javadoc)
         * 
         * @see mt.comm.ServerComm#disconnectClient(java.lang.String)
         */
        @Override
        public void disconnectClient(String nickname) {

                if (clientIsConnected(nickname)) {
                        clients.remove(nickname);
                        ServerMessage msg = new ServerMessage(
                                        mt.comm.ServerSideMessage.Type.DISCONNECTED, nickname, null);
                        addNewMessage(msg);
                }

        }

        /*
         * (non-Javadoc)
         * 
         * @see mt.comm.ServerComm#sendOrder(java.lang.String, mt.Order)
         */
        @Override
        public void sendOrder(String receiversNickname, Order order) {

                try {
                        // check if client is connected
                        if (clientIsConnected(receiversNickname)) {
                                // use same channel to send order
                                ClientMessage msg = new ClientMessage(Type.ORDER, order);
                                ObjectOutputStream outOrder = clients.get(receiversNickname);

                                outOrder.writeObject(msg);
                                outOrder.reset();

                        }
                } catch (IOException e) {
                        System.out.println("Error sending order to client");
                }
        }

        /**
         * Adds the new client.
         * 
         * @param nickname
         *            the nickname
         * @param out
         *            the out
         * @param msg
         *            the msg
         */
        public void addNewClient(String nickname, ObjectOutputStream out,
                        ServerMessage msg) {

                userAlreadyExists = false;
                try {
                        if (clients.containsKey(nickname)) {

                                ClientMessage errorMsg = new ClientMessage(Type.ERROR,
                                                "This nickname already exists");

                                out.writeObject(errorMsg);
                                userAlreadyExists = true;

                        } else {
                                clients.put(nickname, out);
                                addNewMessage(msg);

                        }

                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
        }

        /**
         * Adds the new message.
         * 
         * @param message
         *            the message
         */

        public void addNewMessage(ServerMessage message) {
                messages.add(message);
        }

        /**
         * Gets the messages.
         * 
         * @return the messages
         */
        public BlockingQueue<ServerMessage> getMessages() {
                return messages;
        }

        public ObjectOutputStream getOut() {
                return clientSocketOut;
        }

        /**
         * check if user already exists Assure that user who is connect his orders
         * won't be deleted
         * 
         * @return
         */
        public boolean userAlreadyExists() {
                return userAlreadyExists;
        }
}